//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
namespace LargoCommon.Music
{
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Text;
using System.Xml.Serialization;
using Abstract;
///
/// Binary Schema.
///
[Serializable]
[XmlRoot]
public class BinarySchema : BinaryStructure
{
#region Fields
/// List of places.
private Collection places;
/// List of distances.
private Collection distances;
#endregion
#region Constructors
/// Initializes a new instance of the BinarySchema class.
public BinarySchema() //// resharper - redundant call : base()
{
}
///
/// Initializes a new instance of the BinarySchema class.
///
/// The given system.
/// Structural code.
public BinarySchema(GeneralSystem givenSystem, string structuralCode)
: base(givenSystem, structuralCode) {
Contract.Requires(givenSystem != null);
this.FormalBehavior = new FormalBehavior();
this.RhythmicBehavior = new RhythmicBehavior();
}
///
/// Initializes a new instance of the BinarySchema class.
///
/// The given system.
/// Bit array.
public BinarySchema(GeneralSystem givenSystem, BitArray givenBitArray)
: base(givenSystem, givenBitArray) {
Contract.Requires(givenSystem != null);
this.FormalBehavior = new FormalBehavior();
this.RhythmicBehavior = new RhythmicBehavior();
}
///
/// Initializes a new instance of the BinarySchema class.
///
/// The given system.
/// Number of structure.
public BinarySchema(GeneralSystem givenSystem, long number)
: base(givenSystem, number) {
this.FormalBehavior = new FormalBehavior();
this.RhythmicBehavior = new RhythmicBehavior();
}
/// Initializes a new instance of the BinarySchema class.
/// Binary structure.
public BinarySchema(BinaryStructure structure)
: base(structure) {
Contract.Requires(structure != null);
this.FormalBehavior = new FormalBehavior();
this.RhythmicBehavior = new RhythmicBehavior();
}
#endregion
#region Properties
/// Gets order of system.
/// Property description.
public byte Order => this.GSystem.Order;
/// Gets schema of positions.
/// Property description.
public string PosSchema => this.PlaceString();
/// Gets binary schema of elements.
/// Property description.
[XmlAttribute]
public virtual string ElementSchema {
get {
var s = new StringBuilder();
for (byte e = 0; e < this.GSystem.Order; e++) {
s.Append(this.IsOn(e) ? '1' : '0');
}
//// string se = (from e in Enumerable.Range(0, this.GSystem.Order).Cast()
//// select this.IsOn(e) ? "1":"0") .Aggregate((current, next) => current + ", " + next); */
return s.ToString().Trim();
}
}
#endregion
#region Places and distances
/// Gets positions of nonzero bits.
/// Property description.
[XmlIgnore]
public Collection Places {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.places == null) {
this.places = this.BitPlaces;
}
//// if (this.places == null) {
//// throw new InvalidOperationException("List of places is null."); }
return new Collection(this.places);
}
}
/// Gets distances of nonzero bits.
/// Property description.
[XmlIgnore]
public Collection Distances {
get {
Contract.Ensures(Contract.Result>() != null);
if (this.distances == null) {
this.distances = this.BitDistances;
}
if (this.distances == null) {
throw new InvalidOperationException("List of distances is null.");
}
return new Collection(this.distances);
}
}
/// Gets or sets Tone Level.
/// Property description.
public byte ToneLevel { get; set; }
/// Gets measure of rhythmical motion.
/// Property description.
public float Mobility => this.Properties.ContainsKey(GenProperty.FormalMobility) ? this.Properties[GenProperty.FormalMobility] : 0f;
/// Gets measure of rhythmical density.
/// Property description.
public float Filling => this.Properties.ContainsKey(GenProperty.FormalFilling) ? this.Properties[GenProperty.FormalFilling] : 0f;
/// Gets measure of rhythmical regularity.
/// Property description.
public float Variance => this.Properties.ContainsKey(GenProperty.FormalVariance) ? this.Properties[GenProperty.FormalVariance] : 0f;
/// Gets measure of rhythmical beat.
/// Property description.
public float Beat => this.Properties.ContainsKey(GenProperty.FormalBeat) ? this.Properties[GenProperty.FormalBeat] : 0f;
/// Gets measure of rhythmical balance.
/// Property description.
public float Balance => this.Properties.ContainsKey(GenProperty.FormalBalance) ? this.Properties[GenProperty.FormalBalance] : 0f;
/// Gets measure of rhythmical complexity.
/// Property description.
public float Complexity => this.Properties.ContainsKey(GenProperty.FormalComplexity) ? this.Properties[GenProperty.FormalComplexity] : 0f;
/// Gets list of nonzero bits distances.
/// Property description.
/// Returns value.
public string DistanceSchema {
get {
var s = new StringBuilder();
s.Append("(");
for (byte lev = 0; lev < this.Level; lev++) {
var p = (lev == this.Level - 1) ? string.Empty : ",";
if (lev > 0 && lev < this.Distances.Count) {
s.Append(this.Distances[lev] + p);
}
}
s.Append(")");
return s.ToString();
}
}
#endregion
#region Properties - Behavior
///
/// Gets or sets the harmonic behavior.
///
///
/// The harmonic behavior.
///
public FormalBehavior FormalBehavior { get; set; }
///
/// Gets or sets the harmonic behavior.
///
///
/// The harmonic behavior.
///
public RhythmicBehavior RhythmicBehavior { get; set; }
#endregion
#region Public methods
/// Makes a deep copy of the BinarySchema object.
/// Returns object.
public override object Clone() {
return new BinarySchema(this.GSystem, this.GetStructuralCode);
}
/// Formal position of the given nonzero bit.
/// Requested formal level.
/// Returns value.
public byte PlaceAtLevel(byte level) {
if (level >= this.Places.Count) {
throw new ArgumentException("Incorrect level");
}
return this.Places[level];
}
/// Real position of the given nonzero bit.
/// Requested real level.
/// Returns value.
public short RealPlaceAtLevel(short level) {
short p = 0;
if (this.Level == 0) {
return p;
}
while (level < 0) {
level += this.Level;
p -= this.GSystem.Order;
}
while (level >= this.Level) {
level -= this.Level;
p += this.GSystem.Order;
}
var idx = level % this.Level;
if (idx < this.Places.Count) {
p += this.Places[idx];
}
return p;
}
/// Distance of bit pair on given Level.
/// Requested level.
/// Returns value.
public byte DistanceAtLevel(byte level) {
if (level >= this.Distances.Count) {
throw new ArgumentException("Incorrect level");
}
return (byte)(level < this.Level ? this.Distances[level] : 0);
}
///
/// Range of bit pair on given Level.
///
/// The given level.
///
/// Returns value.
///
public BitRange RangeAtLevel(byte givenLevel) {
if (givenLevel >= this.Places.Count) {
throw new ArgumentException("Incorrect level");
}
if (givenLevel >= this.Level) {
return null;
}
var order = this.GSystem.Order;
var place = this.PlaceAtLevel(givenLevel);
var length = this.DistanceAtLevel(givenLevel);
if (givenLevel == (byte)(this.Level - 1)) {
var diff = (short)(place + length - order);
if (diff > 0 && diff < length) {
length = (byte)(length - diff);
}
}
var range = new BitRange(order, place, length);
return range;
}
/// Range of bit pair on given Level - simplified algorithm.
/// Requested level.
/// Returns value.
public BitRange RangeForLevel(byte level) {
if (level >= this.Places.Count) {
throw new ArgumentException("Incorrect level");
}
var range = new BitRange(this.GSystem.Order, this.PlaceAtLevel(level), this.DistanceAtLevel(level));
return range;
}
/// Returns frequency ratio of given level.
/// Requested level.
/// Given level can exceed modality level.
/// Returns value.
public float RatioForLevel(int level) {
Contract.Requires(this.Level != 0);
if (level % this.Level >= this.Places.Count) {
throw new ArgumentException("Incorrect level");
}
var lev = (byte)(level % this.Level);
//// if (this.Level <= 0) { return r; }
var n = level / this.Level;
//// if (this.Order != 0) { //// was r (nonsense)
var r = (float)Math.Pow(2, n + ((float)this.PlaceAtLevel(lev) / this.Order));
return r;
}
/// Returns index of level containing given bit.
/// Given element/bit.
/// Returns value.
public byte LevelContainingBit(byte givenBit) {
for (byte lev = 0; lev < this.Level; lev++) {
if (this.RangeForLevel(lev).ContainsBit(givenBit)) {
return lev;
}
}
return 0;
}
#endregion
#region String representation
/// List of nonzero bits places.
/// Returns value.
public string PlaceString() {
var s = new StringBuilder();
for (byte lev = 0; lev < this.Level; lev++) {
if (lev >= this.Places.Count) {
continue;
}
s.Append(this.Places[lev]);
if (lev < this.Level - 1) {
s.Append(",");
}
}
return s.ToString();
}
/// String representation of the object.
/// Returns object.
public override string ToString() {
var s = new StringBuilder(base.ToString());
s.Append(" ");
s.Append(this.DistanceSchema);
return s.ToString();
}
#endregion
#region Compute properties
/// Evaluate and set Rhythmic properties.
///
public void ComputeRhythmicProperties() {
this.FormalBehavior.Variance = this.ComputeVariance();
this.FormalBehavior.Balance = this.ComputeBalance();
this.RhythmicBehavior.Filling = this.ComputeFilling();
this.RhythmicBehavior.Beat = this.ComputeBeat();
this.RhythmicBehavior.Mobility = this.ComputeMobility();
this.RhythmicBehavior.Complexity = this.ComputeComplexity();
}
///
/// Writes the behavior to properties.
///
public override void WriteBehaviorToProperties() {
if (this.FormalBehavior != null) {
this.Properties[GenProperty.FormalBalance] = this.FormalBehavior.Balance;
this.Properties[GenProperty.FormalVariance] = this.FormalBehavior.Variance;
this.Properties[GenProperty.FormalEntropy] = this.FormalBehavior.Entropy;
}
if (this.RhythmicBehavior != null) {
this.Properties[GenProperty.FormalFilling] = this.RhythmicBehavior.Filling;
this.Properties[GenProperty.FormalBeat] = this.RhythmicBehavior.Beat;
this.Properties[GenProperty.FormalMobility] = this.RhythmicBehavior.Mobility;
this.Properties[GenProperty.FormalComplexity] = this.RhythmicBehavior.Complexity;
}
}
#endregion
#region Evaluation of properties
///
/// Determine and sets the variance property.
///
/// Returns value.
public float ComputeVariance() { // variational quotient
float variance = 0;
if (this.Level > 1) {
var md = this.MeanDistance();
var value = 0.0F;
for (byte e = 0; e < this.Level; e++) {
value = value + (float)Math.Pow(
Math.Abs(this.DistanceAtLevel(e) - md), 2.0);
}
if (this.Level > 0 && md >= DefaultValue.AfterZero && md <= DefaultValue.LargeNumber) { //// Math.Abs(md) > 0.00001
variance = (float)Math.Sqrt(value / this.Level) / md * 100f; // Level-1
}
}
return variance;
}
///
/// Determine and sets the complexity.
///
/// Returns value.
protected float ComputeComplexity() {
var complexity = 0f;
if (this.Level > 1) {
int value = 0, tv = 0;
for (byte lev = 0; lev < this.Level; lev++) {
var p = this.PlaceAtLevel(lev);
if (p <= 0) {
continue;
}
var d = (byte)MathSupport.GreatestCommonDivisor(this.GSystem.Order, p);
tv++;
if (d < p) {
value++;
}
tv++;
if (d == 1) {
value++;
}
}
if (tv != 0) {
complexity = (float)value / tv * 100.0f;
}
}
return complexity;
}
///
/// Determine and sets the complexity.
///
/// Returns value.
protected float ComputeComplexity2() {
//// the algorithm is not very good ?!
var complexity = 0f;
if (this.Level > 1) {
long value = this.GSystem.Order; // 1L;
for (byte lev = 0; lev < this.Level; lev++) {
long p = this.DistanceAtLevel(lev);
if (p > 0) {
value = (long)MathSupport.LeastCommonMultiple(value, p);
}
}
var denominator = 2.0f * this.GSystem.Order * this.GSystem.Order;
complexity = value / denominator * 100.0f;
if (complexity > 100) {
complexity = 100.0f;
}
}
return complexity;
}
///
/// Mean distance of nonzero bits.
///
///
/// Returns object.
///
protected float MeanDistance() {
//// Contract.Requires(this.GSystem != null);
if (this.Level < 1) {
return 1.0F;
}
var value = this.GSystem.Order / (float)this.Level;
return value;
}
///
/// Determine and sets the mobility property.
///
/// Returns value.
protected float ComputeMobility() {
//// float mobility = this.GSystem.Order != 0 ? (Level / (float)this.GSystem.Order) * 100f : 0;
var mobility = (this.ToneLevel / (float)this.Level) * 100f; //// this.GSystem.Order > 0 ? (this.ToneLevel / (float)this.Level) * 100f : 0;
return mobility;
}
///
/// Determine and sets the filling property.
///
/// Returns value.
protected float ComputeFilling() {
byte sum = 0;
var order = this.GSystem.Order;
var isPause = this.IsOn(0);
for (byte e = 0; e < order; e++) {
if (this.IsOn(e)) {
isPause = false;
}
if (!isPause) {
sum++;
}
}
var filling = order != 0 ? 100.0f * sum / order : 0;
return filling;
}
///
/// Determine and sets the side property.
///
///
/// Returns value.
///
protected float ComputeBalance() {
var median = this.GSystem.Median;
float left = this.IsOnInRange(0, (byte)(median - 1));
//// Contract.Assume(this.GSystem != null);
float right = this.IsOnInRange(median, (byte)(this.GSystem.Order - 1));
var balance = this.Level > 0 ? (DefaultValue.HalfUnit + ((right - left) / this.GSystem.Order)) * 100.0f : 0f;
return balance;
}
///
/// Determine and sets the beat property.
///
/// Returns value.
protected float ComputeBeat() {
var beat = 0F;
if (this.Level > 0) {
float v = 0, tv = 0;
var order = this.GSystem.Order;
for (byte m = 2; m < order; m++) {
if (order % m != 0) {
continue;
}
for (byte e = 0; e < order; e++) {
if (e == 1 || e % m != 0) {
continue;
}
float dv = m;
if (e < this.BitArray.Count && this.BitArray[e]) { //// = this.IsOn(e) (time optimization)
v += dv;
}
tv += dv;
}
}
if (tv >= DefaultValue.AfterZero && tv <= DefaultValue.LargeNumber) { //// Math.Abs(tv) > 0.0001
beat = v / tv * 100.0f;
}
}
return beat;
}
///
/// Determine and sets the entropy property.
///
/// Returns value.
protected float ComputeEntropy() {
//// Contract.Assume(this.GSystem != null);
var entropy = 0f;
if (this.Level > 1) {
var value = 0.0f;
for (byte lev = 0; lev < this.Level; lev++) {
var p = (float)this.DistanceAtLevel(lev) / this.GSystem.Order;
if (p > 0) {
value = (float)(value + (p * Math.Log(p)));
}
}
var logLevel = (float)Math.Log(this.Level);
if (logLevel >= DefaultValue.AfterZero && logLevel <= DefaultValue.LargeNumber) {
entropy = -value / logLevel * 100f;
}
}
return entropy;
}
#endregion
}
}